﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using System.Windows.Forms;
using BLE_Keypad_Tool.Properties;
using InTheHand.Bluetooth;
using InTheHand.Net.Bluetooth;

namespace BLE_Keypad_Tool
{
	public partial class FormMain : Form
	{
		BluetoothDevice target_device;
		GattService service_device_info;
		GattCharacteristic characteristic_uart_rx, characteristic_uart_tx;
		readonly List<byte> received_data = new List<byte>();
		int received_data_length;
		KeyPad keypad;
		FormKeypad _form_keypad;

		public FormMain()
		{
			InitializeComponent();

			try
			{
				var radio = BluetoothRadio.Default;
				buttonRefreshDevice.Enabled = true;
				buttonConnectDevice.Tag = "connect";

				buttonRefreshDevice_Click(null, null);
			}
			catch (PlatformNotSupportedException)
			{
				buttonBluetoothAdaptor.Enabled = false;
			}
		}

		#region System Controls Events
		private void FormMain_Load(object sender, EventArgs e) => AddOwnedForm(_form_keypad);

		private void FormMain_Move(object sender, EventArgs e)
		{
			if (_form_keypad != null)
			{
				_form_keypad.Location = new Point(Left, Top + Height - 6);
			}
		}

		/// <summary>
		/// 刷新设备按键单击事件
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private async void buttonRefreshDevice_Click(object sender, EventArgs e)
		{
			comboDeviceList.Items.Clear();
			buttonRefreshDevice.Enabled = false;
			await refresh_devices();
			buttonRefreshDevice.Enabled = true;
		}

		/// <summary>
		/// 连接设备按键单击事件
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private async void buttonConnectDevice_Click(object sender, EventArgs e)
		{
			if (buttonConnectDevice.Tag.Equals("disconnect"))
			{
				if (target_device.Gatt.IsConnected)
				{
					target_device.Gatt.Disconnect();

					buttonConnectDevice.Tag = "connect";
					buttonConnectDevice.Image = Resources.connect;
				}

				return;
			}

			var (name, id) = Utilities.extract_device_name_and_id(
				comboDeviceList.Items[comboDeviceList.SelectedIndex].ToString());

			buttonConnectDevice.Enabled = false;
			buttonRefreshDevice.Enabled = false;
			comboDeviceList.Enabled = false;
			await connect_to_target_device(name, id);
			await send_command(Constants.COMMAND_QUERY_KEYPAD_CONFIG);
		}

		/// <summary>
		/// 显示本地蓝牙适配信息按键单击事件
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void buttonBluetoothAdaptor_Click(object sender, EventArgs e)
		{
			var radio = BluetoothRadio.Default;
			var address = Utilities.format_device_id(radio.LocalAddress.ToString());

			MessageBox.Show($"制造商：{radio.Manufacturer}\r\n广播名称：{radio.Name}\r\n唯一标识：{address}\r\n模式：{radio.Mode}", "Bluetooth Adaptor", MessageBoxButtons.OK, MessageBoxIcon.Information);
		}

		/// <summary>
		/// 显示设备信息按键单击事件
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private async void buttonDeviceInformation_Click(object sender, EventArgs e)
		{
			var characteristics = await service_device_info.GetCharacteristicsAsync();
			var device_info = await get_device_information(characteristics);

			MessageBox.Show(device_info, "Device Information", MessageBoxButtons.OK, MessageBoxIcon.Information);
		}
		#endregion


		#region Bluetooth Events
		/// <summary>
		/// 处理设备发送过来的数据的回调函数。
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void characteristic_uart_tx_value_changed(object sender, GattCharacteristicValueChangedEventArgs e)
		{
			int length = // 7
				Constants.COMMAND_PREFIX.Length;

			if (Encoding.UTF8.GetString(e.Value, 0, length) == Constants.COMMAND_PREFIX)
			{
				int command = e.Value[length];

				switch (command)
				{
					case Constants.COMMAND_QUERY_KEYPAD_CONFIG:
						length += // 11
							1/*command_length*/ +
							Constants.COMMAND_SEPARATOR.Length/*3*/;

						if (Encoding.UTF8.GetString(
							e.Value, length,
							Constants.COMMAND_BEGIN_TRANSFER.Length) == Constants.COMMAND_BEGIN_TRANSFER)
						{
							length += // 19
								Constants.COMMAND_BEGIN_TRANSFER.Length/*5*/ +
								Constants.COMMAND_SEPARATOR.Length/*3*/;
							received_data.Clear();
							received_data_length = BitConverter.ToInt16(e.Value, length);
						}
						else
						{
							received_data.AddRange(e.Value.Skip(length).ToArray());

							if (received_data.Count == received_data_length)
							{
								string settings_json = Encoding.UTF8.GetString(received_data.ToArray());
								// Console.WriteLine(settings_json);

								BeginInvoke(new MethodInvoker(() =>
								{
									keypad = JsonSerializer.Deserialize<KeyPad>(settings_json);
									_form_keypad = new FormKeypad(keypad)
									{
										Location = new Point(Left, Top + Height - 6),
										Visible = true,
									};
								}));
							}
						}

						break;
					default:
						break;
				}
			}
		}

		/// <summary>
		/// 处理设备断开事件的回调函数。
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void target_device_GattServerDisconnected(object sender, EventArgs e)
		{
			target_device = null;
			service_device_info = null;
			characteristic_uart_rx = null;
			characteristic_uart_tx = null;

			BeginInvoke(new MethodInvoker(() =>
			{
				buttonRefreshDevice.Enabled = true;
				buttonDeviceInformation.Enabled = false;
				buttonConnectDevice.Enabled = true;
				buttonConnectDevice.Tag = "connect";
				buttonConnectDevice.Image = Resources.connect;
				comboDeviceList.Enabled = comboDeviceList.Items.Count > 0;

				if (_form_keypad != null)
				{
					_form_keypad.Close();
					_form_keypad = null;
				}
			}));
		}
		#endregion


		#region Private Task Functions
		/// <summary>
		/// 刷新已配对的设备列表。
		/// </summary>
		/// <returns></returns>
		private async Task refresh_devices()
		{
			bool available = await Bluetooth.GetAvailabilityAsync();

			if (available)
			{
				var paired_devices = await Bluetooth.GetPairedDevicesAsync();

				foreach (var device in paired_devices)
				{
					comboDeviceList.Items.Add($"{device.Name} ({Utilities.format_device_id(device.Id)})");
				}

				comboDeviceList.Enabled = comboDeviceList.Items.Count > 0;

				if (comboDeviceList.Items.Count > 0)
				{
					comboDeviceList.SelectedIndex = 0;
					buttonConnectDevice.Enabled = true;
				}
				else
				{
					MessageBox.Show("No paired device found.", Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Warning);
				}
			}
			else
			{
				MessageBox.Show("Bluetooth adaptor not available.", Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Warning);
			}
		}

		/// <summary>
		/// 连接到指定的设备。
		/// </summary>
		/// <param name="name">设备显示名称</param>
		/// <param name="id">设备 ID，一般为不带分隔符 Mac 地址</param>
		/// <returns></returns>
		private async Task connect_to_target_device(string name, string id)
		{
			target_device = await BluetoothDevice.FromIdAsync(id);
			target_device.GattServerDisconnected += target_device_GattServerDisconnected;

			if (target_device.Name.Equals(name))
			{
				await target_device.Gatt.ConnectAsync();

				if (target_device.Gatt.IsConnected)
				{
					buttonConnectDevice.Tag = "disconnect";
					buttonConnectDevice.Image = Resources.disconnect;
					buttonRefreshDevice.Enabled = false;
					comboDeviceList.Enabled = false;

					var services = await target_device.Gatt.GetPrimaryServicesAsync();
					var uart = services.Where(s => s.Uuid == Constants.UUID_NORDIC_UART).FirstOrDefault();

					if (uart == null)
					{
						MessageBox.Show("No UART Service found in this device.", Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Warning);
						target_device.Gatt.Disconnect();
					}
					else
					{
						var characteristics = await uart.GetCharacteristicsAsync();

						foreach (var characteristic in characteristics)
						{
							if (characteristic.Uuid == Constants.UUID_NORDIC_RX)
							{
								characteristic_uart_rx = characteristic;
							}
							else if (characteristic.Uuid == Constants.UUID_NORDIC_TX)
							{
								characteristic_uart_tx = characteristic;
								characteristic_uart_tx.CharacteristicValueChanged += characteristic_uart_tx_value_changed;
							}
						}

						keypad = new KeyPad();
					}

					var device_info = services.Where(service => service.Uuid == GattServiceUuids.DeviceInformation);

					if (device_info != null)
					{
						service_device_info = device_info.First();
						buttonDeviceInformation.Enabled = true;
					}
				}
				else
				{
					MessageBox.Show("Unable connect to device.", Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Warning);
					target_device.Gatt.Disconnect();
				}
			}
		}

		/// <summary>
		/// 从 DeviceInformation 服务获取设备相关信息。
		/// </summary>
		/// <param name="characteristics">DeviceInformation 服务的特征列表</param>
		/// <returns>整合后的设备信息。</returns>
		private async Task<string> get_device_information(IReadOnlyList<GattCharacteristic> characteristics)
		{
			StringBuilder sb = new StringBuilder();

			foreach (var characteristic in characteristics)
			{
				var result = await characteristic.ReadValueAsync();

				if (characteristic.Uuid == Constants.UUID_MANUFACTURER_NAME)
				{
					sb.AppendLine($"Manufacturer: {Encoding.UTF8.GetString(result)}");
				}
				else if (characteristic.Uuid == Constants.UUID_MODEL_NUMBER)
				{
					sb.AppendLine($"Model Number: {Encoding.UTF8.GetString(result)}");
				}
				else if (characteristic.Uuid == Constants.UUID_SERIAL_NUMBER)
				{
					sb.AppendLine($"Serial Number: {Encoding.UTF8.GetString(result)}");
				}
				else if (characteristic.Uuid == Constants.UUID_FIRMWARE_REVISION)
				{
					sb.AppendLine($"Firmware: {Encoding.UTF8.GetString(result)}");
				}
				else if (characteristic.Uuid == Constants.UUID_HARDWARE_REVISION)
				{
					sb.AppendLine($"Hardware: {Encoding.UTF8.GetString(result)}");
				}
				else if (characteristic.Uuid == Constants.UUID_SOFTWARE_REVISION)
				{
					sb.AppendLine($"Software: {Encoding.UTF8.GetString(result)}");
				}
			}

			return sb.ToString();
		}

		private async Task send_command(int command, string data = "")
		{
			if (characteristic_uart_rx == null) { return; }

			List<byte> list = new List<byte>();
			bool need_response = false;

			list.AddRange(Encoding.UTF8.GetBytes(Constants.COMMAND_PREFIX));
			list.Add((byte)command);

			switch (command)
			{
				case Constants.COMMAND_QUERY_KEYPAD_CONFIG:
					need_response = true;
					break;
				default:
					break;
			}
			
			if (need_response)
			{
				await characteristic_uart_rx.WriteValueWithResponseAsync(list.ToArray());
			}
			else
			{
				await characteristic_uart_rx.WriteValueWithoutResponseAsync(list.ToArray());
			}
		}
		#endregion
	}
}
